//#include "nshead.h"
//#include "mc_pack.h"
#include "shared_ptr.hpp"

#include "ConnQueue.h"
#include "Common.h"
#include "WorkThread.h"
#include "FileDescriptor.h"
#include "WavFile.h"
#include "TTSCaller.h"

#include <iostream>

using namespace std;

// TODO: [liangrongtang] 申请模块后，使用nshead / mcpack进行交互

/* 当前Demo使用自定义格式。
 * Request:
     * header: ID(0x0A0B0C0D) + Version (uint16) + DataLen (uint32)
     * body:
     *      ZH / EN (uint16) 0x00 / 0x01
     *      ENCODING (uint16) 0x00: GBK
     *      TEXT (N)
     *
   Response:
     * header: ID(0x0A0B0C0D) + Version (uint16) + DataLen (uint32)
     * body:
     *      Data (N) mp3 file
 */
const uint32 HEADER_LEN = 10;
const uint32 SERVICE_ID = 0x0A0B0C0D;
const uint32 TYPE_WAV = 0x01;
const uint32 TYPE_MP3 = 0x02;
const uint32 MAX_REQ_SIZE = 1000000;

int WorkThread::ParseReq(std::map<std::string, int>& params,
    string& text, int fd,
    boost::shared_ptr<byte>& buf)
{
    if (fd < 0)
    {
        return -1;
    }

    int len = readn(fd, buf.get(), HEADER_LEN);
    if (len != HEADER_LEN)
    {
        cout << "Failed to read header: " << len << endl;
        return -2;
    }

    const byte* p_buf = buf.get();
    uint32 id = (p_buf[0] << 24) | (p_buf[1] << 16) | (p_buf[2] << 8) | p_buf[3];
    if (id != SERVICE_ID)
    {
        cout << "Invalid ID: " << id << endl;
        return -3;
    }

    uint16 version = (p_buf[4] << 8) | p_buf[5];
    if (version != 0)
    {
        cout << "Invalid version: " << version << endl;
        return -4;
    }
    params["version"] = version;

    uint32 dataLen = (p_buf[6] << 24) | (p_buf[7] << 16) | (p_buf[8] << 8) | p_buf[9];
    printf("DataLen: %u %u %u %u\n", p_buf[6], p_buf[7], p_buf[8], p_buf[9]);
    if (dataLen > MAX_REQ_SIZE)
    {
        cout << "Invalid dataLen: " << dataLen << endl;
        return -5;
    }
    cout << "id = " << id << "; version = " << version << "; dataLen = " << dataLen << endl;

    memset(buf.get(), 0, MAX_REQ_SIZE);
    p_buf = buf.get();
    uint32 bodyLen = dataLen - HEADER_LEN;
    if ((len = readn(fd, buf.get(), bodyLen)) != bodyLen)
    {
        cout << "Failed to recv " << dataLen << " bytes" << endl;
        return -6;
    }
    uint16 lan = (p_buf[0] << 8) | p_buf[1];
    if (lan != 0 && lan != 1)
    {
        cout << "param [lan] invalid: " << lan << endl;
        return -7;
    }
    params["lan"] = lan;

    uint32 encoding = (p_buf[2] << 8) | p_buf[3];
    if (encoding != 0)
    {
        cout << "param [encoding] invalid: " << encoding << endl;
        return -8;
    }
    params["encoding"] = encoding;

    boost::shared_ptr<char> textbuf(new char[dataLen - 4 + 1]);
    memset(textbuf.get(), 0, dataLen - 4 + 1);
    memcpy(textbuf.get(), p_buf + 4, dataLen - 4);

    text = string(textbuf.get());
    cout << "lan = " << lan << "; encoding = " << encoding
         << "; text = " << text << endl;

    return 0;
}

int WorkThread::WriteResponse(int fd, const boost::shared_ptr<Item>& item,
    boost::shared_ptr<byte>& buf, std::map<std::string, int>& params)
{
    if (fd < 0)
    {
        return -1;
    }

    const byte* res = item.get()->_data;
    const uint32 resLen = item.get()->_dataLen;

    memset(buf.get(), 0, MAX_REQ_SIZE);
    buf.get()[0] = 0xA;
    buf.get()[1] = 0xB;
    buf.get()[2] = 0xC;
    buf.get()[3] = 0xD;

    buf.get()[4] = (params["version"] >> 8) & 0xFF;
    buf.get()[5] = params["version"] & 0xFF;

    buf.get()[6] = (resLen >> 24) & 0xFF;
    buf.get()[7] = (resLen >> 16) & 0xFF;
    buf.get()[8] = (resLen >> 8) & 0xFF;
    buf.get()[9] = resLen & 0xFF;

    memcpy(buf.get() + HEADER_LEN, res, resLen);
    int len = 0;
    if ((len = writen(fd, buf.get(), resLen + HEADER_LEN)) != resLen + HEADER_LEN)
    {
        cout << "Failed to write bytes: " << len << endl;
        return -2;
    }
    return 0;
}

void* WorkThread::Process()
{
    boost::shared_ptr<byte> buf(new byte[MAX_REQ_SIZE]);
    boost::shared_ptr<Item> item(new Item());
    while (true)
    {
        memset(buf.get(), 0, MAX_REQ_SIZE);
        item.get()->Init();

        FileDescriptor fd(ConnQueue::Instance()->Pop());
        map<string, int> params;

        string text;
        int ret = ParseReq(params, text, fd.FD(), buf);
        if (ret < 0)
        {
            cout << "Failed to recv req" << endl;
            continue;
        }

        LAN lan = (LAN)params["lan"];
        ENCODING encoding = (ENCODING)params["encoding"];

        item.get()->Init();
        TTSCaller ttsCaller;
        ttsCaller.SetTTSReq(text, lan, encoding);
        ttsCaller.TalkWithTTS(item.get());

        boost::shared_ptr<WavFile> wavFile(WavFile::Create(item.get()->_data,
            item.get()->_dataLen, ENDIAN_BIG));
        if (wavFile.get() == NULL)
        {
            cout << "Invalid wav file" << endl;
            continue;
        }

//        wavFile.get()->Print();

        item.get()->Init();
        wavFile.get()->ToMP3(item);

        cout << "MP3 size = " << item.get()->_dataLen << endl;

        ret = WriteResponse(fd.FD(), item, buf, params);
        if (ret < 0)
        {
            cout << "Failed to write response" << endl;
        }
    }
}
